/**
 * This software was produced for the U. S. Government
 * under Contract No. FA8721-09-C-0002, and is
 * subject to the Rights in Noncommercial Computer Software
 * and Noncommercial Computer Software Documentation Clause
 * (DFARS) 252.227-7014 (JUN 1995)
 * 
 * (c) 2009 The MITRE Corporation. All Rights Reserved.
 *
 * SPMIF to OPNET Translator
 * Author: ewalters
 * Created: 3:35:25 PM, Aug 13, 2007
 * MITRE Corporation
 */
package com.mitre.spmif2opnet.generation.ir;

import java.util.LinkedList;
import java.util.List;

import com.mitre.spmif2opnet.generation.OpnetGraphConverter;
import com.mitre.spmif2opnet.opnet.network.Attr;

/**
 * The most important IR class - OPNET ohases
 * Since the OPNET XML is bereft of structure, we have our own phase IR to hold
 *  the info, and then we generate the XML objects from the data in these
 *  classes.
 * NOTE: On the difference between "name" and "desc"
 *  "Name" corresponds to the node id as described in S-PMIF< i.e. a symbol
 *    that points to a unique node
 *  "Desc" corresponds to the user-readable name of that node
 * In the general case, the system should also use "desc" for the "_prev_id",
 *  which may also be a slippery slope, as it may be difficult to generalize,
 *  i.e. we may have to propagate the "desc" to endpoints!
 * FOLLOWUP NOTE: We have implemented pretty much all of these changes - 
 *  we only need to test to see if the endpoints are set up correctly, 
 *  perhaps once we implement branching phases
 */
public class Phase {
	/**
	 * OPNET Req->Resp strings
	 */
	public enum ReqRespPattern {
		CONCURRENT { 
			public String toString() {
				return "REQ->REQ->....RESP->.... (Concurrent)";
			}
		},
		SERIAL	{		
			public String toString() {
				return "REQ->RESP->REQ->RESP->.... (Serial)";
			}
		};
	}
	
	/**
	 * OPNET phase end designations
	 */
	public enum PhaseEnding {
		FINAL_REQ_LEAVES_SRC {
			public String toString() {
				return "Final Request Leaves Source";
			}
		},		
		FINAL_REQ_ARRIVES_DST {
			public String toString() {
				return "Final Request Arrives at Destination";
			}
		},	
		FINAL_RESP_LEAVES_DST {
			public String toString() {
				return "Final Request Leaves Destination";
			}
		},	
		FINAL_RESP_ARRIVES_SRC {
			public String toString() {
				return "Final Request Arrives at Source";
			}
		};
	}
	
	/**
	 * Phase identifier 
	 */
	private String _id;
	/**
	 *  These next two effectively encode "Start Phase After":
	 *   null = default to "Previous Phase Ends", unless 1st phase,
	 *   in which case = "Application Starts"
	 */
	private Phase _prev_phase;
	private boolean _first_phase;
	/**
	 *  Info concerning the source of the phase
	 */
	private Endpoint _source;
	/** 
	 * Info concerning the destination
	 */
	private Endpoint _dest;
	/**
	 * Traffic-based parameters
	 */
	private ReqRespPattern _rr_pat;
	private PhaseEnding _end;
	/**
	 * Get the string description - for the phase name?
	 */
	private String _name;
	
	/**
	 * Default constructor
	 * @param id the id
	 * @param prev_phase the previous phase
	 */
	public Phase(String id, Phase prev_phase) {
		_id = id;
		_first_phase = false;
		_source = null;
		_dest = null;
		// OPNET defaults
		_rr_pat = ReqRespPattern.CONCURRENT;
		_end = PhaseEnding.FINAL_REQ_ARRIVES_DST;
		_name = null;
		_prev_phase = prev_phase;
	}
	
	/**
	 * Previous phase mutator
	 * @param p the phase
	 */
	public void setPrevPhase(Phase p) {
		_prev_phase = p;
		
	}
	
	/**
	 * Set this as a first phase
	 * @param flag are we?
	 */
	public void setFirstPhase(boolean flag) {
		_first_phase = flag;
	}
	
	/**
	 * Set the source endpoint
	 * @param source the endpoint
	 */
	public void setSource(Endpoint source) {
		_source = source;
	}

	/**
	 * Set the dest endpoint
	 * @param dest the endpoint
	 */
	public void setDestination(Endpoint dest) {
		_dest = dest;
	}

	/**
	 * Set the request-response pattern
	 * @param pat the pattern
	 */
	public void setRRPattern(ReqRespPattern pat) {
		_rr_pat = pat;
	}
	
	/**
	 * Set the phase ending 
	 * @param end the ending
	 */
	public void setPhaseEnding(PhaseEnding end) {
		_end = end;
	}
	
	/**
	 * Set the phase name
	 * @param s
	 */
	public void setName(String s) {
		_name = s;
	}
	
	/**
	 * Unique phase id
	 */
	private static int _phase_id = 0;
	
	/** 
	 * Get a new unused phase identifier 
	 * @return the id
	 */
	public static String getNewPhaseId() {
		String phase_id = "phase" + Integer.toString(_phase_id);
		++_phase_id;
		return phase_id;
	}
	
	/**
	 * Source Endpoint accessor
	 * @return the endpoint
	 */
	public Endpoint getSource() {
		return _source;
	}
	
	/**
	 * Destination Endpoint accessor
	 * @return the endpoint
	 */
	public Endpoint getDest() {
		return _dest;
	}
	
	/**
	 * Name accessor
	 * @return the name
	 */
	public String getName() {
		if (_name == null) {
			return _id;
		} else {
			return _name;
		}
	}
	
	/**
	 * Dump the phase to a string
	 */
	public String toString() {
		return getName() + ": "
			+ (_source == null ? "NONE" : _source.getName()) + " -> "
			+ (_dest   == null ? "NONE" : _dest.getName());
	}
	
	/**
	 * Turn this phase into an OPNET attribute list
	 * @param prefix 
	 * @return the list
	 */
	public List<Attr> getAttributes(String prefix) {
		List<Attr> attrs = new LinkedList<Attr>();
		String s_after;
		if (_prev_phase == null) {
			if (_first_phase) { s_after = "Application Starts"; }
			else { s_after = "Previous Phase Ends"; }
		} else {
			s_after = _prev_phase.getName();
		}
		attrs.add(OpnetGraphConverter.makeAttr(prefix + "Start Phase After", s_after, "true"));
		if (_dest == null) {
			attrs.add(OpnetGraphConverter.makeAttr(prefix + "Destination", "Not Applicable"));
			attrs.add(OpnetGraphConverter.makeAttr(prefix + "Dest->Source Traffic", "No Response", "true"));
		} else {
			attrs.addAll(_dest.getAttributes(prefix, false));
		}
		if (_source == null) {
			attrs.add(OpnetGraphConverter.makeAttr(prefix + "Source", "Not Applicable"));
		} else {
			// TODO: May be better to link this code up at production time
			//  with start reference info - or may be OK!
			//if (_first_phase) { _source.setPointDesignation(Endpoint.SpecialPoint.ORIGINATING_SOURCE); }
			attrs.addAll(_source.getAttributes(prefix, true));
		}
		attrs.add(OpnetGraphConverter.makeAttr(prefix + "REQ/RESP Pattern", _rr_pat.toString(), "true"));
		attrs.add(OpnetGraphConverter.makeAttr(prefix + "End Phase When", _end.toString(), "true"));
		attrs.add(OpnetGraphConverter.makeAttr(prefix + "Phase Name", this.getName() /* _id */));
		return attrs;
	}	
	
	/**
	 * Is this a processing phase
	 * @return is it?
	 */
	public boolean isProcessingPhase() {
		// If both of these are null, we are doing networking
		return (!(_source == null && _dest == null));
	}
}